共计 5233 个字符,预计需要花费 14 分钟才能阅读完成。
一、扫描按键状态
KEY_ScanValue
函数用于扫描按键状态
void KEY_ScanValue(void)
{
// 将 PB0-PB3 设置为输入,其他位不变
key.scanBuf &= 0x00; // 读取 PB0-PB3 的状态,低电平有效
DDRB &= ~0x1E; // 将 PB1-PB3 设为输入
PORTB |= 0x1E; // 使能上拉电阻,PB1-PB3 为高电平
// 清除当前扫描的按键位
PORTB &= ~(1 << key.bit); // 选择当前要扫描的按键(设置为低电平)// 更新扫描缓冲区
// key.scanBuf <<= 1;
// 将 scanBuf 的值左移 1 位,为下一次扫描腾出位置
key.scanBuf |= (~PINB & 0x1E); // 读取 PB1-PB4 的状态,低电平有效
// 检查是否扫描完所有按键
if (++key.bit > 3)
{
key.bit = 0; // 重置 key.bit 为 0
key.readBuf = key.scanBuf; // 将当前的 scanBuf 值存入 readBuf
key.scanBuf = 0; // 清空 scanBuf,为下一次扫描做准备
key.read = 1; // 设置 read 标志,表示新数据可用
}
}
逐步解释
- 初始化和配置 :
key.scanBuf &= 0x00;
:清空扫描缓冲区,确保没有旧数据。DDRB &= ~0x1E;
:将 PB1、PB2 和 PB3 设置为输入模式。PORTB |= 0x1E;
:使能 PB1、PB2 和 PB3 的上拉电阻,确保它们在未按下时为高电平。
- 选择当前按键 :
PORTB &= ~(1 << key.bit);
:将当前扫描的按键设置为低电平,允许读取其状态。
- 更新扫描缓冲区 :
key.scanBuf |= (~PINB & 0x1E);
:读取 PB1、PB2 和 PB3 的状态,低电平有效。通过取反PINB
,可以得到当前按键的状态,并更新scanBuf
。
- 按键扫描完成检查 :
if (++key.bit > 3)
:如果扫描到第四个按键(即 PB3),则重置key.bit
为 0,准备下一轮扫描。key.readBuf = key.scanBuf;
:将当前的扫描结果存入readBuf
,以便后续处理。key.scanBuf = 0;
:清空scanBuf
,为下一次扫描做准备。key.read = 1;
:设置标志位,表示新数据已准备好。
二、按键消抖
void KEY_GetValue(void)
{
unsigned char ucBuf;
if (key.read)
{
key.read = 0;
if (key.readBuf != key.readBakBuf)
{
key.readBakBuf = key.readBuf; // 更新键值
key.delay = 0x00; // 清零按键计数器
key.newTimes = 0x00; // 新键按下计时
key.disable = 0; // 清除按键禁能标志(键值更新须处理按键)}
else
{
key.delay++; // 按键消抖
if (key.delay > key.ditherTimes)
{ // Key Delay
key.validBuf = key.readBuf; // 读取有效键值
if (key.accelMode)
{ // 新键按下总时间
ucBuf = key.newTimes;
}
else
{ // 按键按下总时间
ucBuf = key.onTimes;
}
if (ucBuf > 20)
{ // 高速(6 秒以上 由扫描次数决定速度)key.decode = 1; // 按键译码申请
key.ditherTimes = 3; // 提高按键读取速度
key.delay = 0x00;
}
else if (ucBuf > 10)
{ // 中速(3 秒以上 由扫描次数决定速度)key.decode = 1; // 按键译码申请
key.ditherTimes = 5; // 提高按键读取速度
key.delay = 0x00;
}
else
{ // 低速(正常操作 由定时器决定速度)key.ditherTimes = 10; // 恢复按键速度
if (key.disable)
{key.delay = key.ditherTimes;}
else
{
key.decode = 1; // 按键译码申请
key.disable = 1; // 按键限时防止连续处理
key.delay = 0x00;
}
}
}
}
}
}
KEY_GetValue
函数用于处理按键输入和消抖 逐步解释
- 按键读取标志 :
if (key.read)
: 检查是否有新的按键读取请求。key.read = 0;
: 清除读取标志,准备下一次读取。
- 按键状态变化检测 :
if (key.readBuf != key.readBakBuf)
: 检查当前读取的按键值是否与备份值不同。- 如果不同,更新备份值并重置相关计数器和标志。
- 按键消抖处理 :
- 如果按键值没有变化,增加消抖计数
key.delay++
。 - 当消抖计数超过设定的阈值
key.ditherTimes
时,读取有效的按键值。
- 如果按键值没有变化,增加消抖计数
- 按键时间判断 :
- 根据
key.accelMode
判断当前的计时方式,选择key.newTimes
或key.onTimes
。 - 根据按键按下的总时间
ucBuf
,调整按键读取的速度:- 高速 :如果按下时间超过 20,设置为高速模式。
- 中速 :如果按下时间超过 10,设置为中速模式。
- 低速 :恢复到正常操作速度,并设置防止连续处理的标志。
- 根据
注意事项
- 按键消抖 :确保
key.ditherTimes
的设置合理,以避免误触发。 - 按键时间管理 :根据实际需求,调整
ucBuf
的判断条件,以适应不同的按键响应速度。 - 状态管理 :确保
key
结构体的其他字段(如accelMode
、onTimes
等)在其他部分代码中正确更新和使用。
三、按键解码
void KEY_ValueDecode(void)
{
unsigned char i;
unsigned char code;
unsigned long buffer;
if (key.decode)
{
key.decode = 0;
if (key.validBuf)
{
code = 1; // 按键编码
buffer = key.validBuf;
while (!(buffer & 1))
{
buffer >>= 1;
code++;
}
if (buffer >> 1)
{for (i = 0; i < multipleKey; i++)
{if (key.validBuf == pgm_read_word(keyCodeTable + i))
{ // 有键按下
break;
}
}
code = 13 + i; // none 1bit + int 16bit start 17bit
} // 最后一个为无效按键
}
else
{code = 0;}
if (code > key.code)
{ // 组合键释放时静音
key.tone = 0; // 清除按键音标志(允许再次发按键音)}
key.code = code;
key.prcs = 1;
}
}
KEY_ValueDecode
函数用于解码按键输入并处理相应的操作 逐步解释
- 解码标志检查 :
if (key.decode)
: 检查是否有新的按键解码请求。key.decode = 0;
: 清除解码标志,以准备下一次解码。
- 有效按键检测 :
if (key.validBuf)
: 检查是否有有效的按键值。code = 1;
: 初始化按键编码为 1。buffer = key.validBuf;
: 将有效按键值保存到buffer
中。
- 查找最低有效位 :
while (!(buffer & 1))
: 通过右移操作查找最低有效位,计算按键编码。
- 检查多个按键 :
if (buffer >> 1)
: 检查是否有多个按键被按下。- 遍历
keyCodeTable
,查找当前有效按键值是否存在。 - 如果找到,更新
code
为13 + i
,表示组合键。
- 音效处理 :
if (code > key.code)
: 如果当前按键编码大于之前的编码,清除音效标志key.tone
,允许再次发出按键音。
- 更新状态 :
key.code = code;
: 更新当前按键编码。key.prcs = 1;
: 设置处理标志,表示有新的按键事件需要处理。
注意事项
- 键码表 :确保
keyCodeTable
和multipleKey
已正确初始化,并包含所有可能的按键值。 - 音效管理 :根据具体需求,调整音效处理逻辑,以适应不同的按键交互模式。
- 组合键支持 :确保组合键逻辑与实际硬件设计一致,避免误解码。
四、按键操作和相应功能
void KEY_FunAction(void)
{if (key.prcs)
{
key.prcs = 0; // 按键处理已响应
if (!key.fallPrcs)
{ // 后沿按键 直接调用
if (key.code)
{
key.state = ON; // 按键按下标志
key.offTimes = 0x00;
if (key.code < keyNumber)
{key.restoreBuf = key.validBuf;}
else
{ // 无效按键
key.code = keyNumber;
key.tone = 1; // 禁止按键没完全释放的发音
}
}
else
{
key.state = OFF;
key.onTimes = 0x00;
key.restoreBuf = key.validBuf;
}
}
CALL_FUN_PTR(pgm_read_word(KeyFunList + key.code)); // 按键处理函数调用
}
}
KEY_FunAction
函数用于处理按键操作并调用相应的功能,
逐步解释
- 按键处理标志检查 :
if (key.prcs)
: 检查是否有新的按键事件需要处理。key.prcs = 0;
: 清除处理标志,表示按键事件已响应。
- 后沿按键处理 :
if (!key.fallPrcs)
: 检查是否为后沿按键(即按键释放时)。- 如果是后沿按键,继续处理按键状态。
- 按键状态判断 :
if (key.code)
: 检查按键编码是否有效。- 按键按下 :
- 设置
key.state = ON;
,表示按键处于按下状态。 - 清零
key.offTimes
,重置按键释放计时。 - 如果按键编码小于
keyNumber
,保存当前有效按键值到key.restoreBuf
。 - 如果按键编码无效,设置
key.code
为keyNumber
,并禁止发音(key.tone = 1;
)。
- 设置
- 按键未按下 :
- 设置
key.state = OFF;
,表示按键未按下。 - 清零
key.onTimes
,重置按键按下计时。 - 保存当前有效按键值到
key.restoreBuf
。
- 设置
- 按键按下 :
- 调用功能 :
CALL_FUN_PTR(pgm_read_word(KeyFunList + key.code));
:根据按键编码调用相应的功能处理函数。
注意事项
- 功能指针管理 :确保
KeyFunList
中的功能指针已正确初始化,且与按键编码一一对应。 - 按键状态管理 :确保
key.state
、key.onTimes
和key.offTimes
的使用符合实际需求,以避免状态混乱。 - 音效管理 :根据具体需求,调整音效处理逻辑,以适应不同的按键交互模式。
正文完